#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include<arpa/inet.h>
#include <error.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include "list.h"

struct pthread_arg_st{
	int p_client_id;        // 客户端socket
    pthread_mutex_t mutex;  // 互斥量
	pthread_cond_t cond;    // 条件变量
    int exit;               // 是否退出（客户端断开连接或者recv出错的时候，同时退出读取线程与写入线程）
    char data[1024];        // 数据
    List *client_list;      // 客户端socke集合
};

void list_display(List *list)
{
    for (int i = 0; i < list_count(list); i++)
    {
        int *d = list_data(list, i);
        printf("client id = %d, %d\n", *d, i);
    }
}

// 客户端id在集合中的索引
int get_client_index(List *list,int client_id)
{
    int index = -1;
    for (int i = 0; i < list_count(list); i++)
    {
        int *d = list_data(list, i);
        if(client_id == *d)
        {
            index = i;
            break;
        }
    }
    return index;
}

void* recvsocket(void* arg)//接受来着客户端数据的线程
{
    pthread_t thread_id = pthread_self();
    printf("--server recvsocket thread id: %lu\n", thread_id);
    // int st = *(int*)arg;

    struct pthread_arg_st *args = (struct pthread_arg_st *)arg;
    int st = args->p_client_id;

    char s[1024];
    while (1)
    {
        // 加锁 
        // pthread_mutex_lock(&(args->mutex));  

        memset(s, 0, sizeof(s));
        int rc = recv(st, s, sizeof(s), 0);
        if (rc <= 0)//代表socket被关闭（0）或者出错（-1）
        {
            args->exit = 1; // 关闭读写线程
            printf("sercver recv fail:%d\n", rc);
        }
        
        strcpy(args->data,s);

        // 通知发送线程收到信息
        pthread_cond_broadcast(&(args->cond));

        if(args->exit){
            // recv() 接收到客户端断开或者出错，退出接收线程
            break;
        }

        // 解锁
        // pthread_mutex_unlock(&(args->mutex));

        //printf("server receive:%s\n", s);
    }
    return NULL;
}

void* sendsocket(void* arg)//向客户端socket发送数据的线程
{
    pthread_t thread_id = pthread_self();
    printf("--server sendsocket thread id: %lu\n", thread_id);
    // int st = *(int*)arg;

    struct pthread_arg_st *args = (struct pthread_arg_st *)arg;
    int st = args->p_client_id;
    //printf("server client_id: %d\n", st);

    char s[1024];
    while (1)
    {
        /*
        单个客户端连接一共2个线程，如果发送线程这里提前拿到了锁，接收线程就拿不到锁，就会一直等着，无法执行recv，也就无法通知接收线程
        */
        // 加锁
        // pthread_mutex_lock(&(args->mutex));
        
        // 更改条件并唤醒所有等待线程
        pthread_cond_wait(&(args->cond), &(args->mutex));

        if(args->exit){
            printf("----socket clinet(%d) is closed!\n", st);
            // 将客户端连接从链表中删除
            int index = get_client_index(args->client_list, st);
            list_removeAt(index, args->client_list);
            list_display(args->client_list);
            // 释放条件变量和互斥量
            pthread_cond_destroy(&(args->cond));
            pthread_mutex_destroy(&(args->mutex));
            free(args); // 释放线程参数指针
            // recv() 接收到客户端断开或者出错，退出发送线程
            break;
        }

        int sc;
        int client_count = list_count(args->client_list);
        int distribute_count = client_count-1;
        printf("----distribute clinet count : %d\n", distribute_count);
        for (int i = 0; i < client_count; i++)
        {
            memset(s, 0, sizeof(s));
            int *pclient = list_data(args->client_list, i);
            if(st == (*pclient)){
                // strcat(s, "response clinet count : ");
                // char count[8];
                // sprintf(count, "%d", distribute_count);
                // strcat(s, count);
                strcat(s, "cmd:connect");
                sc = send(st, s, strlen(s), 0); // 这里应该响应已经向多少个设备发送了消息，数量
            } else {
                //strcpy(s,args->data);
                strcat(s, "cmd:disconnect");
                sc = send(*pclient, s, strlen(s), 0);
                printf("server client(%d) send to client(%d), content:%s\n", st, (*pclient), s);
            }
            // printf("server send:%s\n", s);
        }

        // 解锁
        // pthread_mutex_unlock(&(args->mutex));

    }


}
//执行命令 ./server_bus.out 8080
int main(int arg, char* args[])
{
    if (arg < 2)
    {
        printf("arg<2\n");
        return -1;
    }
    int port = atoi(args[1]);
    int st = socket(AF_INET, SOCK_STREAM, 0);

    //setsockopt 设置socket的一个属性，让地址可以重用。
    int on = 0;
    if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsockopt failed:%s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    //INADDR_ANY表示这个服务器上的所有Ip地址。一台服务器可以有多个ip地址。将socket绑定到这个机器的所有ip地址上
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //将ip地址与server程序绑定
    if (bind(st, (struct sockaddr*) & addr, sizeof(addr)) == -1)
    {
        printf("bind fail %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    //server开始监听。 20代表同时有多少个连接过来（20并发）
    if (listen(st, 20) == -1)
    {
        printf("listen fail %s\n", strerror(errno));
        return EXIT_FAILURE;
    }

    List *client_list = NULL; // 客户端连接链表

    client_list = list_create(&client_list);

    //char s[1024];
    int client_st = 0;//客户端socket
    struct sockaddr_in client_addr;//客户端IP
    struct pthread_arg_st *pargs;

    printf("server is runing!\n");

    pthread_t thrd1, thrd2;//定义一个线程
    while (1)
    {
        memset(&client_addr, 0, sizeof(client_addr));
        socklen_t len = sizeof(client_addr);
        //accept会阻塞，直到有客户端连接过来。accept返回客户端的描述符
        client_st = accept(st, (struct sockaddr*) & client_addr, &len);
        if (client_st == -1)
        {
            printf("accept fail %s\n", strerror(errno));
            return EXIT_FAILURE;
        }
        //打印客户端的ip地址
        printf("**accept ip: %s:%d, client id: (%d) \n", inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port), client_st);

        // 保存客户端socket到链表
        list_append((void *)&client_st, client_list, sizeof(int));

        // pthread_create(&thrd1, NULL, recvsocket, &client_st);
        // pthread_create(&thrd2, NULL, sendsocket, &client_st);
        pargs = (struct pthread_arg_st *)malloc(sizeof(struct pthread_arg_st));
        pargs->client_list = client_list;
        pargs->p_client_id = client_st;
        pargs->exit = 0; // 客户端处于连接状态
        pthread_mutex_init(&(pargs->mutex), NULL);
        pthread_cond_init(&(pargs->cond), NULL);
        pthread_create(&thrd1, NULL, recvsocket, (void *)pargs);
        pthread_create(&thrd2, NULL, sendsocket, (void *)pargs);
    }
    close(st);
    getchar();
}